use clap::Parser;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::HashSet;
use std::error::Error;
use std::thread;
use std::time::Duration;
use tokio::runtime::Runtime;

use lettre::message::{header::ContentType, Message};
use lettre::transport::smtp::client::Tls;
use lettre::transport::smtp::SmtpTransport;
use lettre::Transport;
use std::env;
mod toolmgr;
mod calculator;
mod weather;
mod tool;
mod clothing;
mod stock;
use toolmgr::test_tool;

//use crate::stock::test_clothing_guide;
use crate::toolmgr::{build_prompt, extract_tool_call, get_tool_mgr};

// ------------------------------
// Command Line Args
// ------------------------------
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
struct Args {
    #[arg(short, long, default_value = "http://www.coconet.cn:31080/api/messages")]
    api_url: String,
    
    #[arg(short = 's', long, default_value = "www.coconet.cn")]
    smtp_server: String,
    
    #[arg(short = 'p', long, default_value_t = 31025)]
    smtp_port: u16,
    
    #[arg(short = 'r', long, default_value = "l20@coconet.cn")]
    monitor_email: String,
    
    #[arg(short = 'o', long, default_value = "http://127.0.0.1:11434/api/chat")]
    ollama_api_url: String,

    #[arg(short = 'm', long, default_value = "qwen2")]
    ollama_model: String,

    /// Retry count for failed Mailtutan API requests (default: 3)
    #[arg(short = 't', long, default_value_t = 3)]
    retry_count: u8,

    /// Backoff time (seconds) between retries (default: 2)
    #[arg(short = 'b', long, default_value_t = 2)]
    retry_backoff: u64,
}

// ------------------------------
// Ollama API Structures
// ------------------------------
#[derive(Debug, Clone, Serialize, Deserialize)]
struct OllamaMessage {
    role: String,
    content: String,
}

#[derive(Serialize)]
struct OllamaRequest {
    model: String,
    messages: Vec<OllamaMessage>,
    stream: bool,
}

#[derive(Debug, Deserialize)]
struct OllamaResponse {
    message: OllamaMessage,
    done: bool,
}

// ------------------------------
// Xinghuo API Structures
// ------------------------------
#[derive(Debug, Clone, Serialize, Deserialize)]
struct XfMessage {
    role: String,
    content: String,
}

#[derive(Serialize)]
struct XfRequest {
    model: String,
    messages: Vec<XfMessage>,
}

#[derive(Debug, Deserialize)]
struct XfResponse {
    choices: Option<Vec<XfChoice>>,
}

#[derive(Debug, Deserialize)]
struct XfChoice {
    message: XfMessage,
}

// ------------------------------
// Mailtutan Email Structure
// ------------------------------
#[derive(Debug, Deserialize, Clone)]
struct Email {
    id: u32,
    sender: String,
    recipients: Vec<String>,
    subject: String,
    created_at: String,
    attachments: Vec<String>,
    formats: Vec<String>,
}

// ------------------------------
// Retry Helper (for API Requests)
// ------------------------------
async fn with_retry<F, T, E>(
    mut func: F,
    max_retries: u8,
    backoff_sec: u64,
    action: &str,
) -> Result<T, E>
where
    F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + 'static>>,
    E: std::fmt::Display,
{
    let mut retries = 0;
    loop {
        match func().await {
            Ok(result) => return Ok(result),
            Err(e) => {
                if retries >= max_retries {
                    eprintln!("[重试失败] {} (已重试{}次): {}", action, max_retries, e);
                    return Err(e);
                }
                retries += 1;
                eprintln!("[重试中] {} 失败，{}秒后重试（第{}次）: {}", action, backoff_sec, retries, e);
                tokio::time::sleep(Duration::from_secs(backoff_sec)).await;
            }
        }
    }
}

// ------------------------------
// Core: Poll Emails with Client Recreation
// ------------------------------
fn poll_emails(rt: &Runtime, args: &Args) {
    // Use RefCell for thread-safe client replacement (interior mutability)
    let client = RefCell::new(Client::new());
    let mut known_ids = HashSet::new();
    let base_interval = Duration::from_secs(5);
    let mut is_mailtutan_down = false;

    // Initialization: Fetch existing emails
    match rt.block_on(with_retry(
        || {
            let c = client.borrow().clone();
            let api_url = args.api_url.clone();
            Box::pin(async move { fetch_emails(&c, &api_url).await })
        },
        args.retry_count,
        args.retry_backoff,
        "初始化邮件列表",
    )) {
        Ok(emails) => {
            known_ids.extend(emails.iter().map(|e| e.id));
            println!("初始邮件数量: {}", known_ids.len());
        }
        Err(e) => {
            eprintln!("初始化失败: {}", e);
            is_mailtutan_down = true;
            // Recreate client immediately on initial failure
            *client.borrow_mut() = Client::new();
            eprintln!("已重新创建HTTP客户端（初始连接失败）");
        }
    }

    // Continuous polling loop
    loop {
        // Get current client (clone to avoid holding borrow during async)
        let current_client = client.borrow().clone();
        let api_url = args.api_url.clone();
        
        // Fetch emails with retries - FIXED: clone variables before moving to closure
        let fetch_result = rt.block_on(with_retry(
            || {
                // Clone variables inside the FnMut closure before moving to async block
                let client_clone = current_client.clone();
                let url_clone = api_url.clone();
                Box::pin(async move { fetch_emails(&client_clone, &url_clone).await })
            },
            args.retry_count,
            args.retry_backoff,
            "获取邮件列表",
        ));

        match fetch_result {
            Ok(emails) => {
                // Recovered from downtime: Recreate client + re-initialize known IDs
                if is_mailtutan_down {
                    println!("\n✅ Mailtutan 恢复正常！");
                    // Recreate client to reset stale connections
                    *client.borrow_mut() = Client::new();
                    eprintln!("已重新创建HTTP客户端（恢复连接）");
                    
                    // Resync known emails
                    known_ids.clear();
                    known_ids.extend(emails.iter().map(|e| e.id));
                    println!("恢复后邮件总数: {}", known_ids.len());
                    is_mailtutan_down = false;
                }

                // Process new emails
                let current_ids: HashSet<u32> = emails.iter().map(|e| e.id).collect();
                let new_ids: Vec<u32> = current_ids.difference(&known_ids).cloned().collect();

                if !new_ids.is_empty() {
                    println!("\n发现 {} 封新邮件:", new_ids.len());
                    for email in &emails {
                        if new_ids.contains(&email.id) {
                            println!(
                                "ID: {}, 主题: '{}', 发件人: '{}', 时间: {}, 附件: {}",
                                email.id, email.subject, email.sender, email.created_at, email.attachments.join(", ")
                            );
                            known_ids.insert(email.id);

                            // Process monitored emails
                            if email.recipients.contains(&args.monitor_email) {
                                // Use fresh client for plain content fetch
                                let fresh_client = client.borrow().clone();
                                let email_id = email.id.to_string();
                                let api_url = args.api_url.clone();
                                let retry_count = args.retry_count;
                                let retry_backoff = args.retry_backoff;

                                // Fetch plain content with retries
                                let plain_result = rt.block_on(with_retry(
                                    || {
                                        let client = fresh_client.clone();
                                        let id = email_id.clone();
                                        let url = api_url.clone();
                                        Box::pin(async move { fetch_plain(&id, &url, &client).await })
                                    },
                                    retry_count,
                                    retry_backoff,
                                    format!("获取邮件正文 (ID: {})", email_id).as_str(),
                                ));

                                if let Ok(plain_content) = plain_result {
                                    let email_info = format!(
                                        "新邮件通知：\nID: {}\n主题: {}\n发件人: {}\n时间: {}\n内容: {}",
                                        email.id, email.subject, email.sender, email.created_at, plain_content
                                    );

                                    // Call AI and send result (with fresh client)
                                    rt.block_on(async {
                                        let ai_type = match env::var("XF_TOKEN") {
                                            Ok(_) => ("Xinghuo", "星火API"),
                                            Err(_) => ("Ollama", "Ollama API"),
                                        };
                                        let client_clone = client.borrow().clone();
                                        let args_clone = args.clone();
                                        handle_ai_request(
                                            &plain_content, &email_info, ai_type.0, &args_clone, ai_type.1, &client_clone
                                        ).await;
                                    });
                                }
                            }
                        }
                    }
                }
            }
            Err(_) => {
                // Mark down + recreate client if not already done
                if !is_mailtutan_down {
                    eprintln!("\n❌ Mailtutan 连接失败，将持续重试...");
                    // Recreate client to discard stale connections
                    *client.borrow_mut() = Client::new();
                    eprintln!("已重新创建HTTP客户端（检测到服务宕机）");
                    is_mailtutan_down = true;
                }
            }
        }

        thread::sleep(base_interval);
    }
}

// ------------------------------
// Helper: Handle AI Requests + Email Sending
// ------------------------------
async fn handle_ai_request(
    plain_content: &str,
    email_info: &str,
    ai_type: &str,
    args: &Args,
    ai_name: &str,
    client: &Client, // Use fresh client for AI calls
) {
    let is_usage = handle_usage(plain_content, email_info, ai_type, args, ai_name, client).await;
    if(is_usage){
        return;
    }
    let mut tool_result = plain_content.to_string();
    if plain_content.contains("衣服") {
        let result = handle_ai_tool(plain_content, email_info, ai_type, args, ai_name, client).await;
        tool_result = result.clone();
    }else{
        // normal use.
    }

    // Call AI API with fresh client
    let ai_result: Result<String, Box<dyn Error>> = match ai_type {
        "Xinghuo" => with_retry(
            || {
                let info = tool_result.to_string();
                let c = client.clone();
                Box::pin(async move { hello_xf(&info, &c).await })
            },
            args.retry_count,
            args.retry_backoff,
            ai_name,
        ).await,
        "Ollama" => with_retry(
            || {
                let info = tool_result.to_string();
                let c = client.clone();
                let a = args.clone();
                Box::pin(async move { hello_ollama(&info, &a, &c).await })
            },
            args.retry_count,
            args.retry_backoff,
            ai_name,
        ).await,
        _ => {
            eprintln!("未知AI类型: {}", ai_type);
            return;
        }
    };

    // Send AI result via SMTP (always create new SMTP transport)
    if let Ok(ai_resp) = ai_result {
        println!("[{}] 处理结果: {}", ai_name, ai_resp);
        let send_result = with_retry(
            || {
                let info = email_info.to_string();
                let resp = ai_resp.clone();
                let server = args.smtp_server.clone();
                let port = args.smtp_port;
                let ai = ai_type.to_string();
                Box::pin(async move { send_email(&info, &resp, &server, port, &ai).await })
            },
            args.retry_count,
            args.retry_backoff,
            format!("发送{}结果邮件", ai_name).as_str(),
        ).await;

        match send_result {
            Ok(_) => println!("[{}] 结果已发送到Mailtutan", ai_name),
            Err(e) => eprintln!("[{}] 发送邮件失败: {}", ai_name, e),
        }
    }
}

// ------------------------------
// Helper: Handle AI Requests + Email Sending
// ------------------------------
async fn handle_ai_tool(
    plain_content: &str,
    _email_info: &str,
    ai_type: &str,
    args: &Args,
    ai_name: &str,
    client: &Client, // Use fresh client for AI calls
) -> String {
    // Call AI API with fresh client
    let ai_result: Result<String, Box<dyn Error>> = match ai_type {
        "Xinghuo" => with_retry(
            || {
                let info = plain_content.to_string();
                let c = client.clone();
                let prompt = build_prompt(&info);
                Box::pin(async move { hello_xf(&prompt, &c).await })
            },
            args.retry_count,
            args.retry_backoff,
            ai_name,
        ).await,
        "Ollama" => with_retry(
            || {
                let info = plain_content.to_string();
                let c = client.clone();
                let a = args.clone();
                let prompt = build_prompt(&info);
                Box::pin(async move { hello_ollama(&prompt, &a, &c).await })
            },
            args.retry_count,
            args.retry_backoff,
            ai_name,
        ).await,
        _ => {
            eprintln!("未知AI类型: {}", ai_type);
            return plain_content.to_string();
        }
    };

    if let Ok(ai_first_resp) = ai_result {
            // 2. 解析模型输出，判断是否需要调用工具
        if let Some(tool_call) = extract_tool_call(&ai_first_resp) {
            println!("模型输出：{}", ai_first_resp);
            println!("模型决定调用工具：{:?}", tool_call);

            // 3. 调用工具并获取结果
            let tool_manager = get_tool_mgr();
            let tool_result = tool_manager.call(&tool_call);
            println!("工具返回结果：{}", tool_result.content);

            // 4. 将工具结果再次传给模型，生成最终回答
            let final_prompt = format!("工具调用结果：{}\n请根据结果生成自然语言回答。", tool_result.content);
            final_prompt.to_string()
        } else {
            // 不需要调用工具，直接返回模型回答
            println!("解析调用工具答复有问题,返回模型原始回答:{}", ai_first_resp);
            plain_content.to_string()
        }
    }else{
        println!("不需要调用工具,返回模型原始回答");
        plain_content.to_string()
    }

}
// ------------------------------
// Helper: Handle AI Requests + Email Sending
// ------------------------------
async fn handle_usage(
    plain_content: &str,
    email_info: &str,
    ai_type: &str,
    args: &Args,
    ai_name: &str,
    client: &Client, // Use fresh client for AI calls
) -> bool {

    let lower_input = plain_content.to_lowercase();
    
    // 检测"你能做什么"相关提问
    if lower_input.contains("做什么") || lower_input.contains("干什么") || lower_input.contains("功能") || lower_input.contains("用法") || lower_input.contains("教程") 
        || lower_input.contains("可以") && lower_input.contains("什么")
        || lower_input.contains("介绍") && lower_input.contains("自己") {
        let usage =  "我是服装销售助手，能为你提供这些服务：\n\
                1. 库存查询：查询各类衣服的库存情况\n\
                2. 促销信息：告知当前的优惠活动和折扣\n\
                3. 衣服搭配：根据款式、场合提供搭配建议\n\
                你可以直接告诉我你的需求哦~".to_string();
        let send_result = with_retry(
            || {
                let info = email_info.to_string();
                let resp = usage.clone();
                let server = args.smtp_server.clone();
                let port = args.smtp_port;
                let ai = ai_type.to_string();
                Box::pin(async move { send_email(&info, &resp, &server, port, &ai).await })
            },
            args.retry_count,
            args.retry_backoff,
            format!("发送{}结果邮件", ai_name).as_str(),
        ).await;

        match send_result {
            Ok(_) => println!("[{}] 结果已发送到Mailtutan", ai_name),
            Err(e) => eprintln!("[{}] 发送邮件失败: {}", ai_name, e),
        }
        return true;
    }else {
        // do nothing since it's not a usage request.
        return false;
    }
}

// ------------------------------
// Mailtutan API Helpers (use injected client)
// ------------------------------
async fn fetch_emails(client: &Client, base_url: &str) -> Result<Vec<Email>, Box<dyn Error>> {
    let response = client.get(base_url).send().await?;
    if response.status().is_success() {
        Ok(response.json().await?)
    } else {
        let status = response.status();
        let err_text = response.text().await.unwrap_or_else(|_| "未知错误".to_string());
        Err(format!("Mailtutan API 失败 ({}): {}", status, err_text).into())
    }
}

async fn fetch_plain(email_id: &str, base_url: &str, client: &Client) -> Result<String, Box<dyn Error>> {
    let url = format!(
        "{}/{}/plain",
        base_url.trim_end_matches('/'),
        email_id
    );

    let response = client.get(&url).send().await?;
    if response.status().is_success() {
        Ok(response.text().await?)
    } else {
        let status = response.status();
        let err_text = response.text().await.unwrap_or_else(|_| "未知错误".to_string());
        Err(format!("获取邮件正文失败 ({}): URL={}, 详情={}", status, url, err_text).into())
    }
}

// ------------------------------
// AI API Helpers (use fresh client)
// ------------------------------
async fn hello_xf(email_info: &str, client: &Client) -> Result<String, Box<dyn Error>> {
    let url = "https://spark-api-open.xf-yun.com/v1/chat/completions";
    let token = env::var("XF_TOKEN")
        .map_err(|e| format!("读取XF_TOKEN失败: {}", e))?;

    let request_body = XfRequest {
        model: "general".to_string(),
        messages: vec![XfMessage {
            role: "user".to_string(),
            content: format!("{}", email_info),
        }],
    };

    let response = client
        .post(url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&request_body)
        .send()
        .await?;

    let status = response.status();
    if status.is_success() {
        let xf_response: XfResponse = response.json().await?;
        xf_response.choices
            .and_then(|choices| choices.into_iter().next())
            .map(|choice| Ok(choice.message.content))
            .unwrap_or_else(|| Err("星火API返回格式异常".into()))
    } else {
        let err_text = response.text().await.unwrap_or_else(|_| "未知错误".to_string());
        Err(format!("星火API失败 ({}): {}", status, err_text).into())
    }
}

async fn hello_ollama(email_content: &str, args: &Args, client: &Client) -> Result<String, Box<dyn Error>> {
    let request_body = OllamaRequest {
        model: args.ollama_model.clone(),
        messages: vec![OllamaMessage {
            role: "user".to_string(),
            content: format!("{}", email_content),
        }],
        stream: true,
    };

    let mut response = client
        .post(&args.ollama_api_url)
        .header("Content-Type", "application/json")
        .json(&request_body)
        .send()
        .await?;

    let status = response.status();
    if !status.is_success() {
        let err_text = response.text().await.unwrap_or_else(|_| "未知错误".to_string());
        Err(format!("Ollama API失败 ({}): {}", status, err_text).into())
    } else {
        let mut full_response = String::new();
        while let Some(chunk) = response.chunk().await? {
            if let Ok(ollama_resp) = serde_json::from_slice::<OllamaResponse>(&chunk) {
                full_response.push_str(&ollama_resp.message.content);
                if ollama_resp.done {
                    break;
                }
            }
        }
        if full_response.is_empty() {
            Err("Ollama返回空响应".into())
        } else {
            Ok(full_response)
        }
    }
}

// ------------------------------
// SMTP Email Sending (always new transport)
// ------------------------------
async fn send_email(
    _original_email_info: &str,
    ai_result: &str,
    smtp_server: &str,
    smtp_port: u16,
    ai_type: &str,
) -> Result<(), Box<dyn Error>> {
    // 1. Dynamic email content
    let (sender_email, subject, _extra_info) = match ai_type {
        "Xinghuo" => (
            "Xunfei Spark <xf-notifier@coconet.cn>",
            "星火API 邮件处理结果通知",
            "（星火API地址：https://spark-api-open.xf-yun.com/v1/chat/completions）"
        ),
        "Ollama" => (
            "Ollama AI <ollama-notifier@coconet.cn>",
            "Ollama API 邮件处理结果通知",
            "Ollama模型"
        ),
        _ => (
            "AI Notifier <ai-notifier@coconet.cn>",
            "AI 邮件处理结果通知",
            ""
        )
    };

    let bare = format!("{}", ai_result);

    // 2. Build email
    let email = Message::builder()
        .from(sender_email.parse()?)
        .to("Heready <heready.client@coconet.cn>".parse()?)
        .subject(subject)
        .header(ContentType::TEXT_PLAIN)
        .body(bare)?;

    // 3. Create fresh SMTP transport for each send
    let smtp_transport = SmtpTransport::relay(smtp_server)?
        .port(smtp_port)
        .tls(Tls::None)
        .build();

    // 4. Send email
    smtp_transport.send(&email)?;
    Ok(())
}

// ------------------------------
// Main Function
// ------------------------------
fn main() {
    let args = Args::parse();
    println!("===== 程序配置 =====");
    println!("邮件API地址: {}", args.api_url);
    println!("监控邮箱: {}", args.monitor_email);
    println!("SMTP服务器: {}:{}", args.smtp_server, args.smtp_port);
    println!("Ollama API地址: {}", args.ollama_api_url);
    println!("Ollama模型: {}", args.ollama_model);
    println!("星火API启用条件: {}", if env::var("XF_TOKEN").is_ok() { "已设置XF_TOKEN（启用）" } else { "未设置XF_TOKEN（默认使用Ollama）" });
    println!("\n程序已启动，正在监控新邮件...（按Ctrl+C退出）");

    // 1. 创建一个 tokio 异步运行时
    let rt = Runtime::new().expect("无法创建运行时");

    // 2. 阻塞当前线程，等待异步函数 test_tool() 执行完成
    rt.block_on(test_tool());
    // test_clothing_guide();
    // Create Tokio runtime
    let rt = Runtime::new().expect("创建Tokio运行时失败");

    // Start polling thread
    let args_clone = args.clone();
    thread::spawn(move || {
        poll_emails(&rt, &args_clone);
    });

    // Keep main thread alive
    loop {
        thread::sleep(Duration::from_secs(60));
    }
}
    
